package org.tote.aspect;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.tote.model.Audit;
import org.tote.model.AuditOperationType;
import org.tote.model.User;
import org.tote.model.UserLight;
import org.tote.service.AuditService;
import org.tote.service.user.CustomUserDetails;
import org.tote.service.user.UserService;
import org.tote.service.user.UserServiceImpl;

@Component
@Aspect
public class LoggingAspect {

	@Autowired
	UserService userService;

	@Autowired
	AuditService auditService;

	private final Map<String, String> urls = new HashMap<String, String>();

	@Value("#{application.url}")
	private String APPLICATION_URL;

	@PostConstruct
	public void innitUrls() {
		urls.put(User.class.getSimpleName(), "<a href='" + APPLICATION_URL
				+ "profile/{id}'>User</a>");
		urls.put(UserLight.class.getSimpleName(), "<a href='" + APPLICATION_URL
				+ "profile/{id}'>User</a>");
	}

	@Pointcut("execution(* org.tote.dao.BasicCrudDao.save(*))  && args(param)")
	public void save(Object param) {
	}

	@Pointcut("execution(* org.tote.dao.BasicCrudDao.remove(*))  && args(param)")
	public void remove(Object param) {
	}

	@Pointcut("execution(* org.tote.dao.BasicCrudDao.update(*))  && args(param)")
	public void update(Object param) {
	}

	@AfterReturning("save(param) || remove(param) || update(param)")
	public void logChanging(JoinPoint joinPoint, Object param) {
		String className = param.getClass().getSimpleName();
		if (className.equals("Audit")) return;
		String operation = joinPoint.getSignature().getName();
		Audit audit = new Audit();
		long id = -1;
		try {
			id = (Long) param.getClass().getDeclaredMethod("getId").invoke(param);
		} catch (NoSuchMethodException e) {
			try {
				//Content content = (Content) param;
				//id = content.getId();
				id = -2;
			} catch (ClassCastException ex) {
			}
		} catch (SecurityException e) {
		} catch (IllegalArgumentException e) {
		} catch (IllegalAccessException e) {
		} catch (InvocationTargetException e) {
		} catch (ClassCastException e) {
		}

		if (id == -1) return;

		UserLight user = getCurrentAuthorizedUserLight();

		audit.setUser(user);
		audit.setType(getType(operation));
		String link = urls.get(className);
		if (link != null) {
			link = link.replace("{id}", String.valueOf(id));
		} else {
			link = className;
		}
		audit.setComment(String.format("%s was %sd by user %s(id:%d)", link, operation,
				user.getName(), user.getId()));

		auditService.saveAudit(audit);
	}

	public AuditOperationType getType(String operation) {
		if (operation.equals("save"))
			return AuditOperationType.CREATE;
		else if (operation.equals("remove"))
			return AuditOperationType.DELETE;
		else if (operation.equals("update"))
			return AuditOperationType.UPDATE;
		else
			throw new IllegalArgumentException();
	}

	private UserLight getCurrentAuthorizedUserLight() {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (authentication == null) return userService.getMaintenanceUserLight();
		Object principal = authentication.getPrincipal();
		if (UserServiceImpl.GUEST_USER.equals(principal.toString()))
			return userService.getMaintenanceUserLight();
		CustomUserDetails customUserDetails = (CustomUserDetails) principal;
		UserLight userLight = new UserLight();
		userLight.setId(customUserDetails.getId());
		userLight.setName(customUserDetails.getName());
		return userLight;
	}

}
